﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
//字节序：字节数据在内存中存放的顺序
//计算机在存储数据时，是一个一个字节数据进行存储的
//计算机存储的数据至少都是一个字节的数据，也就是除了char类型的数据，其他数据都是多个字节的
//而因为计算机在内存中存储数据是一个一个字节数据进行存储的，所以就要将多个字节的数据彩粉成一个一个字节数据
//而一个数据拆分成一个一个字节数据之后，这些字节数据在内存中的存储顺序就是字节序
//相对的，在左边的字节数据称作高位字节数据
//       在右边的字节数据称作低位字节数据
//大端字节序存储
//将高位字节数据放入低地址，将低位字节数据放入高地址
//小端字节序存储
//将高位字节数据放入高地址，将低位字节数据放入低地址
//&取地址取出的是一个数据的最低地址，也就是起始地址
//那么判断字节序存储的程序
//取出一个数据的地址，也就是取出一个数据的低地址，然后将这个低地址转化为一个char*的地址，然后对其*
//那么就得到该数据的低地址的字节数据
//如果该字节数据是该数据的高位字节数据 -- 大端存储
//如果该字节数据是该数据的低位字节数据 -- 小端存储
//int zjx()
//{
//	int a = 1;
//	if (*(char*)&a == 1)
//	{
//		return 1;
//	}
//	else
//	{
//		return 0;
//	}
//}
//int main()
//{
//	if (zjx())
//	{
//		printf("小端\n");
//	}
//	else
//	{
//		printf("大端\n");
//	}
//	return 0;
//}

//整型提升：计算机的cpu在计算数据时，是至少以一个int的精度进行计算的
//例如char，short类型的数据不够一个int，那么在char和short类型的数据在cpu进行计算时，会将char和short类型的数据
//转换为int然后进行计算
//有符号char和short进行整型提升时，用符号位对高位进行补充
//无符号char和short进行整型提升时：用0对高位进行填充

//操作符的两端的操作数必须是同一类型的数据，如果不是同一类型的数据，而且两边的操作数有一个是>=int的数据类型
//那么就让精度低的数据类型转变为精度高的数据类型，才能进行计算

//字节序：字节数据在内存存储的顺序就做字节序
//计算机在存储数据时，是一个一个字节数据进行存储
//计算机存储的数据至少是一个字节，也就是除了char类型的数据之外，其余的数据都是多个字节的
//因为计算机在存储数据时是一个一个字节数据进行存储，那么就需要将多个字节的数据划分为多个字节数据
//那么一个数据划分为多个字节数据，那么这些字节数据在内存中存储的顺序就做字节序
//相对的，左边的字节数据称作高位字节数据，右边的字节数据称作低位字节数据
//大端字节序：
//将高位字节数据放入低地址，将低位字节数据放入高地址
//小端字节序：
//将高位字节数据放入高地址，将低位字节数据放入低地址
//判断大小端字节序
//&取地址取的是数据的低地址
//取出数据的地址，也就是取出数据的地址，然后将这个地址转换为（char*），然后对其*
//那么访问的这个字节数据，也就是访问了低地址的字节数据
//如果地位地址的字节数据是数据的高位字节数据 -- 大端字节序
//如果低地址的字节数据是数据的低位字字节数据 -- 小端字节序
//也就是高位字节数据在低地址 -- 大端存储
//也就是低位字节数据在低地址 -- 小端存储
//
//在知道大小端之后，还原一个数据，就按大小端字节数据存放的顺序将整个数据还原

//有符号整型，字符型：最高位是符号位，其余位为数值位
//无符号整型，字符型：全部位都为数值位

//根据内存还原一个数据，首先内存存储数据是一个一个字节数据进行存储，那么将数据划分位一个一个字节数据存储后
//根据这些字节数据还原数据，而多个字节数据在内存中存储是有字节序的
//所以根据字节数据在内存中存储的字节序，将这些字节数据组合在一起还原成数据

//整型提升：计算机cpu在处理数据时，至少是以一个int的精度进行计算数据的
//那么char和short类型的数据不够一个int的精度，那么在char和short类型的数据进行计算时，
//计算机会先将chaf和short传变成int的精度进行计算
//有符号char和short整型提升：用符号位对高位进行补充
//无符号char和short整型提升：用0对高位进行填充
//操作符两边的数据类型必须一致，如果操作符两边的操作数数据类型不一致，那么就将精度低的数据类型转变为精度高的数据类型

//因为计算机在存储数据时是将数据划分为一个一个字节数据，然后根据字节序将这些字节数据存放到内存中
//所以当我们拥有字节数据和字节序就可以根据字节数据以及它的字节序来还原整个数据

//有符号：最高位是符号位，其余位是数值为
//无符号：全部位是数值为

//一个char的大小是一个字节也就是八个比特位，里面只能存放8个二进制位。char也是整型家族，char：-128 -- 127
//00000000   --0
//00000001   --1
//00000010   --2
//01111111   --127
//因为char大多数情况下是有符号char，所以上面德二进制序列最高位会被认为是符号位
//而符号位是0那么就代表这组二进制序列表示的是正整数，正整数原码反码补码都一样，
//所以上面这一组的范围是0 -- 127
//10000000  -- -128，因为char里面只能放8个二进制位，所以这一个二进制序列是没办法算的，计算机会自动解析-128
//10000001  -- 11111110 --11111111 --  -127
//10000011  -- 11111100 --11111101 --  -126
//11000000  -- 10111111 --11000000 --  -64
//
//11000001  -- 10111110 --10111111 -- -
//11000010  -- 10111101 --10111110 -- 
//11000011  -- 10111100 --10111101 --
//11111111  -- 10000000 --10000001 -- -1

//假设这些都是char里面存放的整数，而整数在内存中存储是将整数的补码存入内存，假设上面这些就是char所能存储的补码
//因为一个char只有一个字节，一个字节八个比特位，也就是一个char里面可以存放8个二进制序列，char里面存放的是补码
//然后根据上面八个二进制序列的展开从 00000000 -- 11111111 也就可以得到char里面存放的整数是 -128 -- 127
//一个char的极限也就是八个二进制位
//而那么在0111111 -- 127 的极限上继续+，也就是正整数的极限上+，就会变为负整数，也就在0111111 -- 127的极限上继续加
//就会变为1000000 -- -128
//1111111 -- -1 的极限上继续加，也就是在-1的基础上继续+，就会变为 1 000000  但是char里面只能存放八个二进制位
//那么最高位的1就会舍弃，那么就变成了 0000000 也就是在-1的基础上+1 就会变为 0，也就是在-1的基础上继续加就会变为整数

//那么char无论给它多少值，char会形成一个循环，最终都会落在-128 -- 127之间

//因为char里面只能存放八个比特位
//从0111111 -- 127 基础上加+1 就会变成1000000 ---128，就会变成-128
//也就是在正整数0111111 -- 127的极限上继续+ 就会变成负整数
//从1111111 -- -1 基础上+1 就会变成1 0000000 而因为char只能存放8个二进制位，最高位1会被舍弃
//那么就会变成0000000 -- 0，也就是在11111111 -- -1 的基础上继续加那么就会变成正整数
//所以char就会构成一个循环，也就是无论给char赋值什么，都会落在-128 -- 127之间，因为char一共也就八个二进制位
//char的循环：-1 -  0 - 127 - -128 - -1

//大端字节序存储：将高位字节数据放入低地址，将低位字节数据放入高地址
//小端字节序存储：将高位字节数据放入高地址，将低位字节数据放入低地址

//地址也就是一串二进制序列，那么地址也就是一个整数
//那么将地址转变为int类型的数据那么在此基础上+1，然后再将所得结果转变为指针类型的数据 == 地址向前移动了一个字节

//int main()
//{
//	char a = -1;
//	//10000000 00000000 00000000 000000001
//	//11111111 11111111 11111111 111111110
//	//11111111 11111111 11111111 111111111
//	//char只有八个二进制位，而char取的是最低位字节数据
//	//所以char里面放着 1111111
//	//打印时因为是有符号，打印时不够int的精度，整型提升，提升至int的精度
//	//11111111 11111111 11111111 11111111
//	//10000000 00000000 00000000 00000000
//	//10000000 00000000 00000000 00000001
//	signed char b = -1;
//	unsigned char c = -1;
//	//10000000 00000000 00000000 00000001
//	//11111111 11111111 11111111 11111110
//	//11111111 11111111 11111111 11111111
//	//11111111
//	//00000000 00000000 00000000 11111111
//	//255
//	printf("a=%d,b=%d,c=%d", a, b, c);
//	return 0;
//}
//#include <stdio.h>
//int main()
//{
//	char a = -128;
//	//10000000 00000000 00000000 10000000
//	//11111111 11111111 11111111 01111111
//	//11111111 11111111 11111111 10000000
//	//而char只有八个二进制位，而且char只取最低位字节数据也就是存放10000000
//	//11111111 11111111 11111111 10000000
//	printf("%u\n", a);
//	return 0;
//}

//char/short发生整型提升时，如果是有符号char/short用符号位对高位进行补充
//                       如果是无符号char / short用0对高位进行填充
//char/short发生整型提升时，高位补充什么是看char/short是有符号还是无符号
//如果后续打印的时候以有符号打印就将整型提升后的补码作为有符号的补码
//如果后续打印的时候以无符号打印就将整型提升的补码作为无符号的补码
//整型提升是将char/short里面存放的二进制补码进行整型提升
//因为char/short在内存中存的就是整数的二进制补码

//#include <stdio.h>
//int main()
//{
//	char a = 128;
//	//00000000 00000000 00000000 10000000
//	//00000000 00000000 00000000 10000000
//	//00000000 00000000 00000000 10000000
//    //10000000
//	//11111111 11111111 11111111 10000000
//	//
//	printf("%u\n", a);
//	return 0;
//}

//#include <stdio.h>
//int main()
//{
//	char a[1000];
//	int i;
//	for (i = 0; i < 1000; i++)
//	{
//		a[i] = -1 - i;   // -1 -2 -3 ... -128 127 126 ... 0
//	}                   //   0  1  2 ...  127 128 129 ... 254
//	printf("%d", strlen(a)); 
//	return 0;
//}

//char的存储范围在-128 -- 127，并且char的存储是一个循环
//也就是无论给char赋值什么，最后都会落在-128 -- 127这个区间
//0 --127 - +1 - -128 -- -1 - +1 - 0
//因为char只有一个字节的大小，而一个字节有八个比特位，也就是一个字节有八个二进制位
//那么char所能表示的数据用二进制补码展开就是
//char在大部分情况都是有符号char，也就是二进制位的最高位是符号位，其余位是数值位
//00000000  -- 0
//00000001  -- 1
//00000010  -- 2
//00000011  -- 3
//01111111  -- 127
//因为最高位是0，所以是正整数，正整数原码反码补码都一样
//10000000  -- -128
//10000001  -- 11111110 -- 11111111  -- -127
//10000010  -- 11111101 -- 11111110  -- -126 
//10000011  -- 11111100 -- 11111101  -- -125
//11111111  -- 10000000 -- 10000001  -- -1
//因为char只有一个字节，而一个字节又是8个比特位
//8个比特位能表示的二进制补码只有 00000000 --- 11111111    -18 --- -1 --- 0 --- 127
//因为char只有八个比特位
//0111111正整数127的极限那么在整数极限的情况下+1 ==  11111111 -128
//所以在只有八个二进制位的情况下，正整数01111111 == 127 的极限 + 1 == 11111111 == -128
//因为char只有八个比特位
//所以在极限11111111 == -1 的情况下 + 1 == 1 00000000 因为char只有八个比特位，并且char只取最低位字节数据
//那么 11111111 == -1 的情况下 + 1 = 1 00000000 ，那么最高位1就被抛弃了，那么就是 00000000
//11111111 == -1  --+1--  == 00000000
// 
//01111111 == 127  -- +1 -- == 11111111 == -128
//11111111 == -1   -- +1 -- == 00000000 == 0
//所以char无论给他多大的值，都会落在-128 -- 127之间，并且是一个循环
//并且127 + 1 == -128   -1 + 1 == 0
//      0 -- 127 -- -128  -- -1 -- 0

//#include <stdio.h>
//unsigned char i = 0;  
//int main()
//{
//	for (i = 0; i <= 255; i++)
//	{
//		printf("hello world\n");
//	}
//	return 0;
//}//死循环
//#include <stdio.h>
//int main()
//{
//	unsigned int i;
//	for (i = 9; i >= 0; i--)
//	{
//		printf("%u\n", i);
//	}
//	return 0;
//}//死循环

//#include <stdio.h>
//X86环境 ⼩端字节序 
//int main()
//{
//	int a[4] = { 1, 2, 3, 4 };
//	// 01 00 00 00   02 00 00 00   03 00 00 00   04 00 00 00
//	int* ptr1 = (int*)(&a + 1);//int(*)[4] + 1 
//	printf("%d,%d", ptr1[-1], *ptr2);
//	int* ptr2 = (int*)((int)a + 1);
//	//               4        2000000
//	//
//	return 0;
//}

//char类型的数据只有一个字节，也就是char类型的数据只有八个比特位，也就是char类型的数据只有八个二进制位
//而且char只要最低字节
//char是有符号char，也就是二进制位最高位是符号位，其余位是数值位
//那么char里面所能存放的二进制补码的范围就是
//00000000
//01111111
//那么最高位是0表示正 范围是 0 -- 127
//10000000
//11111111
//最高位是1表示负 范围是 -128 -- -1
//在127的基础上 + 1   ==  01111111 + 1 == 10000000 == -128
//在-1的基础上  + 1   ==  11111111 + 1 == 1 00000000 == 00000000
//在-128的基础上 - 1  ==  10000000 - 1 == 01111111 == 127
//在0的基础上   -  1  ==  00000000 - 1 == 11111111 == -1
//那么在char中无论给他多大的值或多小的值，它的范围一定落在-128 -- 127，并且成为一个循环
//127  + 1 == -128
//-1   + 1  == 0
//-128 - 1 == 127
//0    - 1 == -1

//二进制，八进制，十六进制，十进制 
//只是数值的不同表现形式
//最小的正整数是1，那么不足1的数字，我们就用小数来表示0.xxxxxx
//也就是说如果一个数字够1，那么再么用1表示，再么进1
//在十进制中逢十进一，在十进制的小数情况下也是逢十进一
//那么10个0.1就是1
//那么0.1可以用10^-1 来表示
//那么10个0.01 就是 0.1
//那么0.01 就可以用 10^-2 来表示
//10^-1  10^-2  10^-3  ......  10^负无穷  
//这都是10进制小数点处的位权
//也就是10^-n 就可以到达小数点后第n位

//类比到二进制
//二进制是逢二进一
//也就是说明小数点后一位，当这个位上满足数值1的时候就应该进一
//无论什么样的进制，只要够一就应该往+1
//而二进制的位只有0 or 1，那么二进制小数位也是只能0or1，那么在二进制小数位第一位
//0当然表示0，那么1表示：因为二进制是逢二进一，那么当第一个小数点位为1时候，那么对它再+1 就应该满足逢二进一
//那么当第一个小数点位进一时，那么表明此时小数点后面的数够数值1，那么也就是在二进制小数点第一位为1时对它再+1
//就会满足数值1，那么就可以证明0.1在二进制中表示十进制中的0.5，也就是2^-1，这一位的位权是2^-1
//那么二进制小数点的位权就应该是 2^-1 2^-2 2^-3 ..... 2^负无穷

//任意的二进制浮点数V都可以用科学计数法表示
//(-1)^S * M * 2^E
//S取0 or 1  表示二进制浮点数的+-
//1 <= M < 2
//E表示在(-1)^S * M 的基础上 * 2的多少次方才可以得到原来的浮点数

//浮点数在内存中存储时
//float：4个字节 -- 32个比特位 
//第一个比特位用来存放S，也就是S == 0 时为+ S == 1时为 -
//接下来的八个比特位用来存放E,而这里规定必须存放无符号整数，在存储E时，将E + 127进行存储，这样就可以避免E为负数的情况
//接下里的32个比特位存放M，因为M的整数部分永远为1，那么就不存M的整数部分1了，取出来的时候加上1就行
//那么这32个比特位就可以全部存放M的小数部分，就可以提高精度，直接按照正序对二进制小数值进行存放
//double：八个字节 -- 64个比特位
//第一个比特位用来存放S，S == 0 表示 + ;S == 1 表示-
//接下来的八个比特位用来存放E，这里存放的规定也是存放无符号整数，在存储E时，将E + 1023 然后再进行存储
//那么接下来53个比特位用来存放M，因为M的整数部分都是1，所以不存也罢去，取出的时候+1就行
//那么这53个比特位就全部存放M的小数部分，提高精度,直接按照正序对二进制的小数点值进行存储

//浮点数在拿出来时
//1.E的部分 不全为0 or 不全为 1（正常情况）
//  首先读第一个比特位判断+-，得到S
//  然后读接下来的八个比特位，然后将读到的值 -127 or 1023，然后得到E的值
//  然后读剩下的32 /53 个比特位，将这些小数点后面的值读出来 然后再 + 1，得到E
//  最后(-1)^S * M * 2^E,得到最后的浮点数

//2.E的部分 全部为0
//  首先先读第一个比特位，判断+-，获得S
//  然后再将接下来的八个比特位所得到的E 直接作为 1- 127 or 1  - 1023
//  然后接下来读剩下的32 / 53个比特位，将这些小数点后面的数读出来，不用再+1 获得E
//  (-1)^S * M * 2^-126 --> 无穷小，趋近于0

//3.E的部分 全部为1
//  首先读第一个比特位，判断+-，获得S
//  然后接下来的八个比特位取出的值直接作为E
//  然后接下来读剩下的32 / 53个比特位，然后读出小数部分直接作为M
//  (-1)^S * M * 2^255 --> 无穷大
//  

//#include <stdio.h>
//int main()
//{
//	int n = 9;
//	//00000000 00000000 00000000 00001001
//	//00000000 00000000 00000000 00001001
//	//00000000 00000000 00000000 00001001
//	float* pFloat = (float*)&n;
//	//将int*的指针赋值给float*的指针变量，float指针变量*也是*4个字节
//	printf("n的值为：%d\n", n);
//	//9
//	printf("*pFloat的值为：%f\n", *pFloat);
//	//S == 0
//	//E == 1 - 127 == -126
//	//00000000000000000001001
//	//(-1)S * 0.00000000000000000001001 * 2^-126
//	//这直逼无穷下，并且%f只打印小数点后六位
//	//0.000000
//	*pFloat = 9.0;
//	//9.0 == 1001.0
//	//(-1)^S * M * 2E == (-1)^0 * 1.001 * 2^3
//	//S == 0
//	//M == 1.001
//	//E == 3
//	//0 10000010 00100000000000000000000
//	//
//	printf("n的值为：%d\n", n);
//	//01000001000100000000000000000000
//	printf("*pFloat的值为：%f\n", *pFloat);
//	//9.0
//	//S == 0
//	//E == 130 - 127 == 3
//	//M == 0.001 + 1 == 1.001
//	//(-1)^0 * 1.001 * 2^3
//	return 0;
//}

//二进制，八进制，十进制，十六进制都只是数值的不同表现形式
//十进制：逢十进一，无论是整数部分还是小数部分都是逢10进1
//       那么整数部分的位权就是10^0,10^1,10^2,10^3 ...  10^无穷次方
//       也就是一个数值到达这个位权的要求就会在该位权上显示
//       那么小数部分的位权就是10^-1,10^-2,10^-3 ... 10^负无穷次方
//二进制：逢二进一，无论是二进制的整数部分还是小数部分都是逢二进一
//二进制：逢二进一，无论是二进制的整数部分还是二进制的小数部分都是逢二进一
//       那么二进制整数部分的位权2^0 2^1 2^2 2^3 ... 2^正无穷
//       也就是一个数值用二进制表示，该数值到达二进制的一个位权后，就会在二进制位权的位上显示
//       二进制小数点第一位，因为二进制小数部分也是逢二进一，所以第一个小数点逢二也应该进一
//       那么也就是当二进制小数点第一位为1时，再加+1就应该进一，也就是会成为成为正整数1
//       那么此时二进制小数点第一位1就表示数值0.5也就是2^-1
//       以此类推二进制小数点的位权应该是2^-1 2^-2 2^-3 ... 2^-无穷
